In most cases, your application closes a file after a user clicks in a window's close box or chooses the Close command in the File menu. The Close menu command should be active only when there is actually an active window on the desktop. If there is an active window, you need to determine whether it belongs to your application; if so, you need to handle dialog windows and document windows differently, as illustrated in Listing 1-15 .
Listing 15 Handling the Close menu command
FUNCTION DoCloseCmd: OSErr;
VAR
myWindow: WindowPtr;
myData: MyDocRecHnd;
myErr: OSErr;
BEGIN
myErr := FALSE;
myWindow := FrontWindow; {get window to be closed}
CASE MyGetWindowType(myWindow) OF
kDAWindow:
CloseDeskAcc(WindowPeek(myWindow)^.windowKind);
kMyModelessDialog:
HideWindow(myWindow); {for dialogs, hide the window}
kMyDocWindow:
BEGIN
myData := MyDocRecHnd(GetWRefCon(myWindow));
myErr := DoCloseFile(myData);
IF myErr = noErr THEN
DisposeWindow(myWindow);
END;
OTHERWISE
;
END;
DoCloseCmd := myErr;
END;
The DoCloseCmd function determines the type of the frontmost window by calling the application-defined function MyGetWindowType . (See the chapter "Window Manager" in Inside Macintosh: Macintosh Toolbox Essentials for a definition of MyGetWindowType .) If the window to be closed is a window belonging to a desk accessory, DoCloseCmd closes the desk accessory. If the window to be closed is a dialog window, this procedure just hides the window. If the window to be closed is a document window, DoCloseCmd retrieves its document record handle and calls both DoCloseFile (defined in Listing 1-16 ) and DisposeWindow . Before you close the file associated with a window, you should check whether the contents of the window have changed since the last time the document was saved. If so, you should ask the user whether to save those changes. Listing 1-16 illustrates one way to do this.
FUNCTION DoCloseFile (myData: MyDocRecHnd): OSErr;
VAR
myErr: OSErr;
myDialog: DialogPtr; {pointer to modal dialog box}
myItem: Integer; {item selected in alert box}
myPort: GrafPtr; {the original graphics port}
CONST
kSaveChangesDialog = 129; {resource of Save changes dialog}
BEGIN
IF myData^^.windowDirty THEN {see whether window is dirty}
BEGIN
myItem := CautionAlert(kSaveChangesDialog, NIL);
IF myItem = iCancel THEN{user clicked Cancel}
BEGIN
DoCloseFile := usrCanceledErr;
Exit(DoCloseFile);
END;
IF myItem = iSave THEN
myErr := DoSaveCmd;
END;
IF myData^^.fileRefNum <> 0 THEN
BEGIN
myErr := FSClose(myData^^.fileRefNum);
IF myErr = noErr THEN
BEGIN
myErr := FlushVol(NIL, myData^^.fileFSSpec.vRefNum);
myData^^.fileRefNum := 0; {clear the file reference number}
END;
END;
{Dispose of TextEdit record and controls here (code omitted).}
DisposeHandle(Handle(myData)); {dispose of document record}
DoCloseFile := myErr;
END;
If the document is an existing file that has not been changed since it was last saved, your application can simply call the FSClose function. This routine writes to disk any unwritten data remaining in the volume buffer. The FSClose function also updates the information maintained on the volume for that file and removes the access path. The information about the file is not actually written to the disk, however, until the volume is flushed, ejected, or unmounted. To keep the file information current, it's a good idea to follow each call to FSClose with a call to the FlushVol function.
If the contents of an existing file have been changed, or if a new file is being closed for the first time, your application can call the Dialog Manager routine CautionAlert (specifying a resource ID of an 'ALRT' template) to ask the user whether or not to save the changes. If the user decides not to save the file, you can just call FSClose and dispose of the window. Otherwise, DoCloseFile calls the DoSaveCmd function to save the file to disk.